#include "EstrategiaRecursoEscrituraDiferida.h"
#include "Almacenamiento.h"
#include "NodoBuffer.h"
#include "Archivo.h"

EstrategiaRecursoEscrituraDiferida::EstrategiaRecursoEscrituraDiferida(Archivo* archivo, cantBytes tamanioBloque):EstrategiaRecurso(archivo) {
	this->tamanioBloque = tamanioBloque;
	this->cantMaxBuffers = TAMANIO_MAX_BUFFERS;
}

EstrategiaRecursoEscrituraDiferida::~EstrategiaRecursoEscrituraDiferida() {

	buffers.clear();

	list<NodoBuffer*>::iterator it = buffersLibres.begin();

	while (it != buffersLibres.end()) {
		NodoBuffer* buffer = (*it);
		if (buffer->getEstado() == ESCRITURA_DIFERIDA) {
			offset offset = buffer->getNumBloque() * this->tamanioBloque;
			archivo->escribir(buffer->getDatos(),offset);
		}
		it++;
		delete buffer;
	}
}

void EstrategiaRecursoEscrituraDiferida::escribir(const Bytes& bytes, offset offset) {


	if (offset % tamanioBloque != 0) {
		cerr << "EstrategiaEscrituraDiferida::escribir: offset no valido" << endl;
	}

	if (bytes.getTamanio() != tamanioBloque) {
		cerr << "EstrategiaEscrituraDiferida::escribir: leyendo cantidad de bytes no validos" << endl;
	}

	if (offset >= getTamanioArchivo()) {
		setTamanioArchivo(getTamanioArchivo() + tamanioBloque);
	}

	int numBloque = offset / tamanioBloque;
	NodoBuffer* buffer = buscarBuffer(numBloque);

	if (buffer == NULL) {
		buffer = obtenerOCrearBuffer(numBloque,bytes);
		buffer->setEstado(ESCRITURA_DIFERIDA);
		buffersLibres.push_back(buffer);
	} else {
		buffer->setEstado(OCUPADO);
		buffer->setDatos(bytes);
		buffer->setEstado(ESCRITURA_DIFERIDA);
	}
}

Bytes EstrategiaRecursoEscrituraDiferida::leer(offset offset, cantBytes cantBytes) {

	if (offset % tamanioBloque != 0) {
		cerr << "EstrategiaEscrituraDiferida::leer: offset no valido" << endl;
	}

	if (cantBytes != tamanioBloque) {
		cerr << "EstrategiaEscrituraDiferida::leer: leyendo cantidad de bytes no validos" << endl;
	}

	int numBloque = offset / tamanioBloque;
	NodoBuffer* bufferActual = buscarBuffer(numBloque);

	if (bufferActual != NULL) {
		return bufferActual->getDatos();
	}

	// no encontro ningun Buffer
	Bytes datos = archivo->leer(offset,cantBytes);

	int tamanio = datos.getTamanio();

	bufferActual = obtenerOCrearBuffer(numBloque,datos);
	bufferActual->setEstado(VALIDO);
	buffersLibres.push_back(bufferActual);

	return datos;
}

offset EstrategiaRecursoEscrituraDiferida::getTamanioArchivo() {

	return this->archivo->getTamanio();
}

string EstrategiaRecursoEscrituraDiferida::getPath() {

	return this->archivo->getPath();
}

bool EstrategiaRecursoEscrituraDiferida::setTamanioArchivo(cantBytes nuevo) {

	EstrategiaRecurso::setTamanioArchivo(nuevo);

	return true;
}

void EstrategiaRecursoEscrituraDiferida::imprimirBuffer() {

	list<NodoBuffer*>::iterator it = buffersLibres.begin();

	cout << endl << endl << "************* IMPRESION CONTENIDO BUFFER DIFERIDO***************" << endl << endl;

	    while (it != buffersLibres.end()) {
			NodoBuffer* buffer = *it;
			cout << buffer->toString() << endl;
			it++;
		}

	cout << endl;
	cout << endl << endl << "************* FIN IMPRESION CONTENIDO BUFFER DIFERIDO***********" << endl << endl;
}

NodoBuffer* EstrategiaRecursoEscrituraDiferida::buscarBuffer(int numBloque) {
	list<NodoBuffer*>::iterator it = buffersLibres.begin();
	while (it != buffersLibres.end()) {
		NodoBuffer* buffer = *it;
		if (buffer->getNumBloque() == numBloque) {
			return buffer;
		}
		it++;
	}
	return NULL;
}

NodoBuffer* EstrategiaRecursoEscrituraDiferida::obtenerOCrearBuffer(int numBloque,const Bytes& dato) {

	NodoBuffer* buffer;

	if (buffers.size() < this->cantMaxBuffers) {
		buffer = new NodoBuffer(numBloque, this->tamanioBloque, dato);
		buffers.push_back(buffer);

	} else {

		buffer = buffersLibres.front();
		buffersLibres.pop_front();

		if (buffer->getEstado() == ESCRITURA_DIFERIDA) {
			unsigned offsetAEscribir = buffer->getNumBloque() * this->tamanioBloque;
			this->archivo->escribir(buffer->getDatos(), offsetAEscribir);
		}
	}

	buffer->setNumBloque(numBloque);
	buffer->setDatos(dato);
	buffer->setEstado(OCUPADO);
	return buffer;
}
